package com.crazymaker.springcloud.websocket.session;

import com.crazymaker.push.platform.contract.util.MsgUtils;
import com.crazymaker.springcloud.common.exception.BusinessException;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Data
public final class SessionManager {


    /**
     * 单例
     */
    private SessionManager() {
    }

    private static class SessionMapHolder {
        private static SessionManager singleInstance = new SessionManager();

    }

    public static SessionManager getSingleton() {
        return SessionMapHolder.singleInstance;
    }

    //会话集合
    private ConcurrentHashMap<String, ServerSession> sessionMap = new ConcurrentHashMap<>();


    /**
     * 和 channel 通道实现双向绑定
     */

    public ServerSession add(ServerSession session) {

        session.bindChannel();
        sessionMap.put(session.getId(), session);
        log.info("用户登录:sid={},name={},在线总数: {} ",
                session.getId(), session.getUser().getUsername(), sessionMap.size());
        return session;
    }

    /**
     * 获取session对象
     */
    public ServerSession getSession(String sessionId) {
        if (sessionMap.containsKey(sessionId)) {
            return sessionMap.get(sessionId);
        } else {
            return null;
        }
    }

    /**
     * 删除session
     */
    public void removeSession(ServerSession session) {
        if (null == session) {
            return;
        }
        sessionMap.remove(session.getId());
        session.unbindChannel();
        session.closeChannel();
        log.info("用户下线:id={}, 在线总数:{}", session.getUser().getUserId(), sessionMap.size());
    }


    /**
     * 关闭连接， 关闭前发送一条通知消息
     */
    public void closeSessionAfterAck(ServerSession session, String echo) {
        ChannelFuture sendFuture = session.getChannel().writeAndFlush(new TextWebSocketFrame(echo));
        sendFuture.addListener(new ChannelFutureListener() {
            public void operationComplete(ChannelFuture future) {
                log.debug("last package send finished:{},channel will closed", echo);

                removeSession(session);

            }
        });

    }

    /**
     * 关闭连接
     */
    public void closeSession(ServerSession session) {
        if (null == session) {
            return;
        }
        session.close();


    }

    /**
     * 发送消息
     *
     * @param ctx 上下文
     * @param msg 待发送的消息
     */
    public void sendMsg(ChannelHandlerContext ctx, String msg) {
        ChannelFuture sendFuture = ctx.writeAndFlush(new TextWebSocketFrame(msg));
        sendFuture.addListener(f ->
        {
            log.debug("send finished:{}", msg);
        });
    }

    public boolean isOnline(String appId, String account) {
        String sessionId = MsgUtils.getSessionId(account, appId);

        if (sessionMap.containsKey(sessionId)) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * 发送消息
     *
     * @param appId
     * @param account
     * @param msg     待发送的消息
     */
    public void syncSendMsg(String appId, String account, String msg) {

        String sessionId = MsgUtils.getSessionId(account, appId);
        ServerSession session = sessionMap.get(sessionId);
        if (null == session || !session.getChannel().isActive()) {
            log.error("session of {} is null or not alive", account);
            throw BusinessException.builder().errMsg("channel is ont ok").build();
//            return false;
        }

        Channel channel = session.getChannel();
        try {
            channel.writeAndFlush(new TextWebSocketFrame(msg)).sync();

            log.debug("send finished:{}", msg);

        } catch (InterruptedException e) {
            log.error("session of {} is null or not alive", account);
//            e.printStackTrace();
            throw BusinessException.builder().errMsg("channel is ont ok").build();

        }

    }
}
